package zju.ccnt.fileserverProxy.repository;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.JacksonJsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Repository;
import zju.ccnt.fileserverProxy.exception.FileHashValueNotFound;
import zju.ccnt.rest.api.ApiCreateResource;

import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * Created by zha on 14/11/28.
 */
@Repository
public class FileUploadRepository {

    private RedisTemplate<String, String> redisTemplate;
    //todo 并发问题 解决
    private JedisConnectionFactory connection;
    //读取上传对应的用户资源信息,某次资源的某个最终形态
    private RedisTemplate<String, ApiCreateResource> redisFileTargetTemplate;
    //资源的贡献上传用户Id
    private RedisTemplate<String, String> redisSuccessUploadersTemplate;
    //资源的上传详情
    private RedisTemplate<String, String> redisUploadDetailTemplate;

    //todo 分库问题
    private final int fileUploadersDbIndex = 0;
    private final int fileUploadDetailDBIndex = 0;
    private final int fileTempWaitingDbIndex = 0;
    private final int defaultDbIndex = 0;

    //redis key prefix组成
    private final String targetBusiness = "target.business:";
    private final String uploadInitial = "upload.initial:";
    private final String attempUploader = "uploader.attemp:";

    private String buildTargetBusinessKey(String hashValue){
        return targetBusiness + hashValue;
    }

    private String buildUploadInitialKey(String hashValue, String businessId){
        return uploadInitial + hashValue + ":" + businessId;
    }

    private String buildAttempUploadersKey(String hashValue, String businessId){
        return attempUploader + hashValue + ":" + businessId;
    }


    @Autowired
    public FileUploadRepository(RedisTemplate<String, String> redisTemplate){
        this.redisTemplate = redisTemplate;
        this.connection = (JedisConnectionFactory)redisTemplate.getConnectionFactory();

        this.redisFileTargetTemplate = new RedisTemplate<String, ApiCreateResource>();
        redisFileTargetTemplate.setConnectionFactory(this.connection);
        redisFileTargetTemplate.setValueSerializer(new JacksonJsonRedisSerializer<ApiCreateResource>(ApiCreateResource.class));
        redisFileTargetTemplate.setKeySerializer(new StringRedisSerializer());
        redisFileTargetTemplate.afterPropertiesSet();

        this.redisSuccessUploadersTemplate = new RedisTemplate<String, String>();
        redisSuccessUploadersTemplate.setConnectionFactory(connection);
        redisSuccessUploadersTemplate.afterPropertiesSet();

        this.redisUploadDetailTemplate = new RedisTemplate<String, String>();
        redisUploadDetailTemplate.setConnectionFactory(connection);
        redisUploadDetailTemplate.afterPropertiesSet();

    }

    public Set<String> getSuccessUploaders(String hashValue){
        connection.setDatabase(fileUploadersDbIndex);
        Set<String> uploaders = redisTemplate.opsForSet().members(hashValue+"_uploaders");
        if (uploaders == null || uploaders.size() == 0) throw new FileHashValueNotFound(hashValue+"_uploaders");
        connection.setDatabase(defaultDbIndex);
        return uploaders;
    }

    public Map<String, String > getFileUploadDetail(String fileHashValue){
        connection.setDatabase(fileUploadDetailDBIndex);
        BoundHashOperations<String, String, String> hashOperations = redisTemplate.boundHashOps(fileHashValue);
        Map<String, String> res = hashOperations.entries();
        connection.setDatabase(defaultDbIndex);
        return res;
    }

    public void uploadedResource(String hashValue){
        connection.setDatabase(fileTempWaitingDbIndex);
        //等待该resource的businessId
        Set<String> businessIds = redisTemplate.opsForSet().members(buildTargetBusinessKey(hashValue));
        //删除试图上传者信息
        for (String business: businessIds){
            redisTemplate.delete(buildAttempUploadersKey(hashValue, business));
        }
        redisTemplate.delete(buildTargetBusinessKey(hashValue));
        connection.setDatabase(defaultDbIndex);
    }

    public ApiCreateResource removeResourceWaitingForPost(String hashValue, String businessId){
        connection.setDatabase(fileTempWaitingDbIndex);
        String key = buildUploadInitialKey(hashValue, businessId);
        ApiCreateResource info = redisFileTargetTemplate.opsForValue().get(key);
        redisTemplate.delete(key);
        connection.setDatabase(defaultDbIndex);
        return info;
    }

    /**
     * 添加等待hashValue对应的resource上传的商家ID
     * @param hashValue
     * @param businessId
     */
    public void addResourceToBusiness(String hashValue, String businessId){
        connection.setDatabase(fileTempWaitingDbIndex);
        redisTemplate.opsForSet().add(buildTargetBusinessKey(hashValue), businessId);
        connection.setDatabase(defaultDbIndex);
    }

    /**
     * 添加等待用户初始化上传的商家资源信息
     * @param hashValue
     * @param businessId
     * @param info
     */
    public void addResourceWaitingForPost(String hashValue, String businessId, ApiCreateResource info){
        connection.setDatabase(fileTempWaitingDbIndex);
        redisFileTargetTemplate.opsForValue().set(buildUploadInitialKey(hashValue, businessId), info);
        connection.setDatabase(defaultDbIndex);
    }

    /**
     * 添加试图上传某商家资源的用户ID
     * @param hashValue
     * @param businessId
     * @param uploader
     */
    public void addAttempUploaders(String hashValue, String businessId, String uploader){
        connection.setDatabase(fileTempWaitingDbIndex);
        redisTemplate.opsForSet().add(buildAttempUploadersKey(hashValue, businessId), uploader);
        connection.setDatabase(defaultDbIndex);
    }

    public Set<String> getBusinessIds(String hashValue){
        connection.setDatabase(fileTempWaitingDbIndex);
        Set<String> businessIds = redisTemplate.opsForSet().members(buildTargetBusinessKey(hashValue));
        connection.setDatabase(defaultDbIndex);
        return businessIds;
    }

    public ApiCreateResource getInitialPostInfo(String hashValue, String businessId){
        connection.setDatabase(fileTempWaitingDbIndex);
        ApiCreateResource info = redisFileTargetTemplate.opsForValue().get(buildUploadInitialKey(hashValue, businessId));
        connection.setDatabase(defaultDbIndex);
        return info;
    }

    public Set<String> getAttempUploaders(String hashValue, String businessId){
        connection.setDatabase(fileTempWaitingDbIndex);
        Set<String> attemps = redisTemplate.opsForSet().members(buildAttempUploadersKey(hashValue, businessId));
        connection.setDatabase(defaultDbIndex);
        return attemps;
    }

}
